# 需求分析:
#
# 1. sql = "select * from t_book" 2. 调用数据库工具方法
# result = exe_sql(sql) print("结果:", result)

import pymysql

# DBUti是Database Utility(数据库工具)，这是常见的命名约定，表示该类用于封装数据库操作
class DBUtil():

    # 初始化
    __conn = None # __conn和__cursor是私有属性，Python会将其重命名为_DBUtil__conn
    __cursor = None

# 创建工具类 用@classmethod是为了后面无需先创建实例直接通过类名调用比如DBUtil.exe_sql()
    @classmethod
    def __get_conn(cls): # 类方法的第一个参数必须是cls代表类本身不是实例，通过cls可访问类属性cls.__conn
        if cls.__conn is None: # 检查类属性 __conn 是否为 None（即是否已存在数据库连接）
            cls.__conn = pymysql.connect(host='localhost',
                                       port=3306,user='root',
                                       passwd='root',
                                       db='books')
            return cls.__conn # 方便后续方法通过__get_conn()获取连接不需要重复创建

    # 获取游标
    @classmethod
    def __get_cursor(cls):
        if cls.__cursor is None:
            cls.__cursor = cls.__get_conn().cursor()
        return cls.__cursor

    # 执行sql
    @classmethod
    def exe_sql(cls, sql):
        cursor = None
        try:
            # 获取游标
            cursor = cls.__get_cursor() # 返回的游标赋值给局部变量cursor
            # 调用游标对象的execute方法，执行sql
            cursor.execute(sql) # 直接使用局部变量cursor
            # sql.strip()是移除字符串sql首尾的空白字符，避免用户输入的sql因缩进或换行导致判断失误
            # lower()将字符串转换为全小写，确保不区分大小写匹配，sql关键字通常大小写等效
            # startswith('select')判断字符串是否以'select'开头
            if sql.strip().lower().startswith('select'):
                # fetchall()用户获取sql查询结果的所有行，返回一个包含所有记录的列表，列表中的每个元素是一个元组，对应一行数据
                # fetchmany(size=n) 可以获取指定数量
                return cursor.fetchall()
            else:
                cls.__conn.commit()
                return cursor.rowcount
        except Exception as e:
            cls.__conn.rollback()
            print(f"SQL执行失败：{e}")
        finally:
            cls.__cursor.close()

    @classmethod
    def __close_cursor(cls):
        if cls.__cursor:
            cls.__cursor.close()
            cls.__cursor = None

    @classmethod
    def __close_conn(cls):
        if cls.__conn:
            cls.__conn.close()
            cls.__conn = None



